package ai.people.netmon.auth.handler;

import ai.people.core.grpc.handler.GrpcExceptionHandler;
import ai.people.netmon.framework.exception.enums.AuthExceptionEnum;
import ai.people.netmon.framework.exception.enums.CommonEnum;
import ai.people.netmon.framework.exception.enums.ParamExceptionEnum;
import ai.people.netmon.framework.exception.type.AuthException;
import ai.people.netmon.framework.exception.type.CustomizeRuntimeException;
import ai.people.netmon.framework.model.response.Result;
import ai.people.netmon.framework.utils.ResultUtil;
import feign.FeignException;
import io.grpc.StatusRuntimeException;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.stream.Collectors;

/**
 * 身份验证异常处理程序
 *
 * @author yuanqinglong
 * @date 2022/2/15 17:34
 */
@Order(1)
@RestControllerAdvice
@RequiredArgsConstructor
public class AuthExceptionHandler {

    Logger log = LoggerFactory.getLogger(AuthExceptionHandler.class);

    private final GrpcExceptionHandler grpcExceptionHandler;

    /**
     * 身份验证异常
     *
     * @param authException 身份验证异常
     * @return {@link Result}<{@link T}>
     */
    @ExceptionHandler(AuthException.class)
    public <T> Result<T> authException(AuthException authException) {
        //记录日志
        log.error("authException exception", authException);
        return ResultUtil.error(authException);
    }



    /**
     * 自定义运行时异常
     *
     * @param customizeRuntimeException 自定义运行时异常
     * @return {@link Result}<{@link T}>
     */
    @ExceptionHandler(CustomizeRuntimeException.class)
    public <T> Result<T> customizeRuntimeException(CustomizeRuntimeException customizeRuntimeException) {
        //记录日志
        log.error("customizeRuntimeException exception", customizeRuntimeException);
        return ResultUtil.error(customizeRuntimeException);
    }

    /**
     * 校验异常
     *
     * @param validException 有效异常
     * @return {@link Result}<{@link T}>
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public <T> Result<T> validException(MethodArgumentNotValidException validException) {
        //记录日志
        String errorMsg = validException.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.joining(","));
        // TODO: 2022/1/7 返回参数校验失败错误码
        log.error("validException exception", validException);
        return ResultUtil.error(ParamExceptionEnum.INSUFFICIENT_PARAMETERS.getCode(), errorMsg);
    }


    /**
     * 约束违反例外
     *
     * @param constraintViolationException 有效异常
     * @return {@link Result}<{@link T}>
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public <T> Result<T> constraintViolationException(ConstraintViolationException constraintViolationException) {
        //记录日志
        String errorMsg = constraintViolationException.getConstraintViolations().stream().map(ConstraintViolation::getMessageTemplate).collect(Collectors.joining(","));
        log.error("constraintViolationException exception", constraintViolationException);
        return ResultUtil.error(ParamExceptionEnum.PARAMETER_PARSING_EXCEPTION.getCode(), errorMsg);
    }

    /**
     * 方法不支持异常
     *
     * @param methodNotSupportedException 方法不支持异常
     * @return {@link Result}<{@link T}>
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public <T> Result<T> methodNotSupportedException(HttpRequestMethodNotSupportedException methodNotSupportedException) {
        String requestMethod = methodNotSupportedException.getMethod();
        log.error("methodNotSupportedException exception", methodNotSupportedException);
        return ResultUtil.error(CommonEnum.REQUEST_METHOD_TYPE_ERROR.getCode(), CommonEnum.REQUEST_METHOD_TYPE_ERROR.getMessage() + requestMethod);
    }


    /**
     * 假装例外
     *
     * @param feignException 异常
     * @return {@link Result}<{@link T}>
     */
    @ExceptionHandler(FeignException.class)
    public <T> Result<T> feignException(FeignException feignException) {
        String message = feignException.getMessage();
        log.error("feignException 异常", feignException);
        if (message.contains("error_description")) {
            return ResultUtil.error(AuthExceptionEnum.AUTH_ACCOUNT_ERROR);
        }
        return ResultUtil.error(CommonEnum.SERVER_ERROR);
    }

    /**
     * 错误处理程序
     *
     * @param exception 异常
     * @return {@link Result}<{@link ?}>
     */
    @ExceptionHandler(InternalAuthenticationServiceException.class)
    public Result<?> internalAuthenticationServiceException(InternalAuthenticationServiceException exception) {
        log.error("内部服务认证异常", exception);
        if (exception.getCause() instanceof StatusRuntimeException) {
            return grpcExceptionHandler.statusRuntimeException((StatusRuntimeException) exception.getCause());
        }
        return ResultUtil.error(AuthExceptionEnum.INTERNAL_SERVICE_EXCEPTION);
    }

    /**
     * 错误处理程序
     *
     * @param exception 异常
     * @return {@link Result}<{@link ?}>
     */
    @ExceptionHandler(InsufficientAuthenticationException.class)
    public Result<?> errorHandler(InsufficientAuthenticationException exception) {
        log.error("bad_credentials", exception);
        return ResultUtil.error(AuthExceptionEnum.INVALID_CREDENTIALS);
    }

    /**
     * 身份验证异常
     *
     * @param exception 异常
     * @return {@link Result}<{@link ?}>
     */
    @ExceptionHandler(AuthenticationException.class)
    public Result<?> authenticationException(AuthenticationException exception) {
        log.error("authenticationException", exception);
        if (exception instanceof InternalAuthenticationServiceException) {
            this.internalAuthenticationServiceException((InternalAuthenticationServiceException) exception.getCause());
        }
        return ResultUtil.error(AuthExceptionEnum.INVALID_CREDENTIALS);
    }

    /**
     * 无效客户端异常
     *
     * @param exception 异常
     * @return {@link Result}<{@link ?}>
     */
    @ExceptionHandler(InvalidClientException.class)
    public Result<?> invalidClientException(InvalidClientException exception) {
        log.error("invalidClientException", exception);
        return ResultUtil.error(AuthExceptionEnum.UNAUTHORIZED_GRANT_TYPE);
    }


    /**
     * throwable
     *
     * @param throwable 异常
     * @return {@link Result}<{@link T}>
     */
    @ExceptionHandler(Throwable.class)
    public <T> Result<T> throwable(Throwable throwable) {
        log.error("系统异常", throwable);
        return ResultUtil.error(CommonEnum.SERVER_ERROR);
    }


}
